package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"github.com/magiconair/properties"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"super_exporter/exporters"
	"super_exporter/test"
	"super_exporter/kafka"
	"time"
)

var (
	Version       = "1.0.0.dev"
	listenAddress = flag.String("web.listen-address", ":9601", "Address to listen on for web interface and telemetry.")
	metricPath    = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	landingPage   = []byte("<html><head><title>Example Exporter" + Version + "</title></head><body><h1>Example Exporter" + Version + "</h1><p><ahref='" + *metricPath + "'>Metrics</a></p></body></html>")
	config *properties.Properties
	configFile string
)

func loadConfigFile(file string){
	confTmp,err := properties.LoadFile(file,properties.UTF8)
	if err != nil {
		log.Fatal("read config file error")
	}
	config = confTmp
}

func makeTestExporter() prometheus.Collector {
	exporter := test.NewExporter()
	// 将 exporters 注册⼊ prometheus，prometheus 将定期从 exporters 拉取数据
	return exporter
}

func makeRedisExporter() prometheus.Collector {
	redisPwd := config.GetString("redis.password","")
	redisPwdFile := config.GetString("redis.password-file","")
	scriptPath := config.GetString("script","")
	tlsClientKeyFile := config.GetString("tls-client-key-file","")
	tlsClientCertFile := config.GetString("tls-client-cert-file","")
	tlsCaCertFile := config.GetString("tls-ca-cert-file","")
	connectionTimeout := config.GetString("connection-timeout","15s")
	redisMetricsOnly := config.GetBool("redis-only-metrics",false)
	pingOnConnect := config.GetBool("ping-on-connect",false)

	passwordMap := make(map[string]string)
	var err error

	if redisPwd == "" && redisPwdFile != "" {
		passwordMap, err = exporters.LoadPwdFile(redisPwdFile)
		if err != nil {
			log.Fatalf("Error loading redis passwords from file %s, err: %s", redisPwdFile, err)
		}
	}

	var ls []byte
	if scriptPath != "" {
		if ls, err = ioutil.ReadFile(scriptPath); err != nil {
			log.Fatalf("Error loading script file %s    err: %s", scriptPath, err)
		}
	}

	var tlsClientCertificates []tls.Certificate
	if (tlsClientKeyFile != "") != (tlsClientCertFile != "") {
		log.Fatal("TLS client key file and cert file should both be present")
	}
	if tlsClientKeyFile != "" && tlsClientCertFile != "" {
		cert, err := tls.LoadX509KeyPair(tlsClientCertFile, tlsClientKeyFile)
		if err != nil {
			log.Fatalf("Couldn't load TLS client key pair, err: %s", err)
		}
		tlsClientCertificates = append(tlsClientCertificates, cert)
	}

	var tlsCaCertificates *x509.CertPool
	if tlsCaCertFile != "" {
		caCert, err := ioutil.ReadFile(tlsCaCertFile)
		if err != nil {
			log.Fatalf("Couldn't load TLS Ca certificate, err: %s", err)
		}
		tlsCaCertificates = x509.NewCertPool()
		tlsCaCertificates.AppendCertsFromPEM(caCert)
	}

	to, err := time.ParseDuration(connectionTimeout)
	if err != nil {
		log.Fatalf("Couldn't parse connection timeout duration, err: %s", err)
	}

	exp, err := exporters.NewRedisExporter(
		config.GetString("redis.addr","redis://localhost:6379"),
		exporters.Options{
			User:                  config.GetString("redis.user",""),
			Password:              config.GetString("redis.password",""),
			PasswordMap:           passwordMap,
			Namespace:             config.GetString("namespace","redis"),
			ConfigCommandName:     config.GetString("config-command","CONFIG"),
			CheckKeys:             config.GetString("check-keys",""),
			CheckSingleKeys:       config.GetString("check-single-keys",""),
			CheckKeysBatchSize:    config.GetInt64("check-keys-batch-size",1000),
			CheckKeyGroups:        config.GetString("check-key-groups",""),
			MaxDistinctKeyGroups:  config.GetInt64("max-distinct-key-groups",100),
			CheckStreams:          config.GetString("check-streams",""),
			CheckSingleStreams:    config.GetString("check-single-streams",""),
			CountKeys:             config.GetString("count-keys",""),
			LuaScript:             ls,
			InclSystemMetrics:     config.GetBool("include-system-metrics",false),
			SetClientName:         config.GetBool("set-client-name",true),
			IsTile38:              config.GetBool("is-tile38",false),
			ExportClientList:      config.GetBool("export-client-list",false),
			ExportClientsInclPort: config.GetBool("export-client-port",false),
			SkipTLSVerification:   config.GetBool("skip-tls-verification",false),
			ClientCertificates:    tlsClientCertificates,
			CaCertificates:        tlsCaCertificates,
			ConnectionTimeouts:    to,
			RedisMetricsOnly:      redisMetricsOnly,
			PingOnConnect:         pingOnConnect,
		},
	)

	if err != nil {
		return nil
	}
	return exp
}

func register(service string,config *properties.Properties){
	switch service {
	case "test":
		prometheus.MustRegister(makeTestExporter())
	case "redis":
		prometheus.MustRegister(makeRedisExporter())
	case "kafka":
		prometheus.MustRegister(kafka.MakeRedisExporter(config))
	default:

	}
}

func main() {
	flag.Parse()

	loadConfigFile("app.properties")
	services := strings.Split(config.GetString("services",""), ",")
	for _, value := range services {
		fmt.Println(value)
		register(value,config)
	}

	http.Handle(*metricPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write(landingPage)
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))

}
